this.$   F
last analyzed

Complexity

Conditions 22
Paths 16

Size

Total Lines 91
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 60
nc 16
nop 2
dl 0
loc 91
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like this.$ often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
3
 * return the resulting jQuery object.
4
 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
5
 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
6
 *  @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
7
 *    criterion ("applied") or all TR elements (i.e. no filter).
8
 *  @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
9
 *    Can be either 'current', whereby the current sorting of the table is used, or
10
 *    'original' whereby the original order the data was read into the table is used.
11
 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
12
 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be 
13
 *    'current' and filter is 'applied', regardless of what they might be given as.
14
 *  @returns {object} jQuery object, filtered by the given selector.
15
 *  @dtopt API
16
 *
17
 *  @example
18
 *    $(document).ready(function() {
19
 *      var oTable = $('#example').dataTable();
20
 *
21
 *      // Highlight every second row
22
 *      oTable.$('tr:odd').css('backgroundColor', 'blue');
23
 *    } );
24
 *
25
 *  @example
26
 *    $(document).ready(function() {
27
 *      var oTable = $('#example').dataTable();
28
 *
29
 *      // Filter to rows with 'Webkit' in them, add a background colour and then
30
 *      // remove the filter, thus highlighting the 'Webkit' rows only.
31
 *      oTable.fnFilter('Webkit');
32
 *      oTable.$('tr', {"filter": "applied"}).css('backgroundColor', 'blue');
33
 *      oTable.fnFilter('');
34
 *    } );
35
 */
36
this.$ = function ( sSelector, oOpts )
37
{
38
	var i, iLen, a = [], tr;
39
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
40
	var aoData = oSettings.aoData;
41
	var aiDisplay = oSettings.aiDisplay;
42
	var aiDisplayMaster = oSettings.aiDisplayMaster;
43
44
	if ( !oOpts )
45
	{
46
		oOpts = {};
47
	}
48
49
	oOpts = $.extend( {}, {
50
		"filter": "none", // applied
51
		"order": "current", // "original"
52
		"page": "all" // current
53
	}, oOpts );
54
55
	// Current page implies that order=current and fitler=applied, since it is fairly
56
	// senseless otherwise
57
	if ( oOpts.page == 'current' )
58
	{
59
		for ( i=oSettings._iDisplayStart, iLen=oSettings.fnDisplayEnd() ; i<iLen ; i++ )
60
		{
61
			tr = aoData[ aiDisplay[i] ].nTr;
62
			if ( tr )
63
			{
64
				a.push( tr );
65
			}
66
		}
67
	}
68
	else if ( oOpts.order == "current" && oOpts.filter == "none" )
69
	{
70
		for ( i=0, iLen=aiDisplayMaster.length ; i<iLen ; i++ )
71
		{
72
			tr = aoData[ aiDisplayMaster[i] ].nTr;
73
			if ( tr )
74
			{
75
				a.push( tr );
76
			}
77
		}
78
	}
79
	else if ( oOpts.order == "current" && oOpts.filter == "applied" )
80
	{
81
		for ( i=0, iLen=aiDisplay.length ; i<iLen ; i++ )
82
		{
83
			tr = aoData[ aiDisplay[i] ].nTr;
84
			if ( tr )
85
			{
86
				a.push( tr );
87
			}
88
		}
89
	}
90
	else if ( oOpts.order == "original" && oOpts.filter == "none" )
91
	{
92
		for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
93
		{
94
			tr = aoData[ i ].nTr ;
95
			if ( tr )
96
			{
97
				a.push( tr );
98
			}
99
		}
100
	}
101
	else if ( oOpts.order == "original" && oOpts.filter == "applied" )
102
	{
103
		for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
104
		{
105
			tr = aoData[ i ].nTr;
106
			if ( $.inArray( i, aiDisplay ) !== -1 && tr )
107
			{
108
				a.push( tr );
109
			}
110
		}
111
	}
112
	else
113
	{
114
		_fnLog( oSettings, 1, "Unknown selection options" );
115
	}
116
117
	/* We need to filter on the TR elements and also 'find' in their descendants
118
	 * to make the selector act like it would in a full table - so we need
119
	 * to build both results and then combine them together
120
	 */
121
	var jqA = $(a);
122
	var jqTRs = jqA.filter( sSelector );
123
	var jqDescendants = jqA.find( sSelector );
124
125
	return $( [].concat($.makeArray(jqTRs), $.makeArray(jqDescendants)) );
126
};
127
128
129
/**
130
 * Almost identical to $ in operation, but in this case returns the data for the matched
131
 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
132
 * rather than any descendants, so the data can be obtained for the row/cell. If matching
133
 * rows are found, the data returned is the original data array/object that was used to  
134
 * create the row (or a generated array if from a DOM source).
135
 *
136
 * This method is often useful in-combination with $ where both functions are given the
137
 * same parameters and the array indexes will match identically.
138
 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
139
 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
140
 *  @param {string} [oOpts.filter=none] Select elements that meet the current filter
141
 *    criterion ("applied") or all elements (i.e. no filter).
142
 *  @param {string} [oOpts.order=current] Order of the data in the processed array.
143
 *    Can be either 'current', whereby the current sorting of the table is used, or
144
 *    'original' whereby the original order the data was read into the table is used.
145
 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
146
 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be 
147
 *    'current' and filter is 'applied', regardless of what they might be given as.
148
 *  @returns {array} Data for the matched elements. If any elements, as a result of the
149
 *    selector, were not TR, TD or TH elements in the DataTable, they will have a null 
150
 *    entry in the array.
151
 *  @dtopt API
152
 *
153
 *  @example
154
 *    $(document).ready(function() {
155
 *      var oTable = $('#example').dataTable();
156
 *
157
 *      // Get the data from the first row in the table
158
 *      var data = oTable._('tr:first');
159
 *
160
 *      // Do something useful with the data
161
 *      alert( "First cell is: "+data[0] );
162
 *    } );
163
 *
164
 *  @example
165
 *    $(document).ready(function() {
166
 *      var oTable = $('#example').dataTable();
167
 *
168
 *      // Filter to 'Webkit' and get all data for 
169
 *      oTable.fnFilter('Webkit');
170
 *      var data = oTable._('tr', {"filter": "applied"});
171
 *      
172
 *      // Do something with the data
173
 *      alert( data.length+" rows matched the filter" );
174
 *    } );
175
 */
176
this._ = function ( sSelector, oOpts )
177
{
178
	var aOut = [];
179
	var i, iLen, iIndex;
0 ignored issues
show
Unused Code introduced by
The variable iIndex seems to be never used. Consider removing it.
Loading history...
180
	var aTrs = this.$( sSelector, oOpts );
181
182
	for ( i=0, iLen=aTrs.length ; i<iLen ; i++ )
183
	{
184
		aOut.push( this.fnGetData(aTrs[i]) );
185
	}
186
187
	return aOut;
188
};
189
190
191
/**
192
 * Add a single new row or multiple rows of data to the table. Please note
193
 * that this is suitable for client-side processing only - if you are using 
194
 * server-side processing (i.e. "bServerSide": true), then to add data, you
195
 * must add it to the data source, i.e. the server-side, through an Ajax call.
196
 *  @param {array|object} mData The data to be added to the table. This can be:
197
 *    <ul>
198
 *      <li>1D array of data - add a single row with the data provided</li>
199
 *      <li>2D array of arrays - add multiple rows in a single call</li>
200
 *      <li>object - data object when using <i>mData</i></li>
201
 *      <li>array of objects - multiple data objects when using <i>mData</i></li>
202
 *    </ul>
203
 *  @param {bool} [bRedraw=true] redraw the table or not
204
 *  @returns {array} An array of integers, representing the list of indexes in 
205
 *    <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to 
206
 *    the table.
207
 *  @dtopt API
208
 *
209
 *  @example
210
 *    // Global var for counter
211
 *    var giCount = 2;
212
 *    
213
 *    $(document).ready(function() {
214
 *      $('#example').dataTable();
215
 *    } );
216
 *    
217
 *    function fnClickAddRow() {
218
 *      $('#example').dataTable().fnAddData( [
219
 *        giCount+".1",
220
 *        giCount+".2",
221
 *        giCount+".3",
222
 *        giCount+".4" ]
223
 *      );
224
 *        
225
 *      giCount++;
226
 *    }
227
 */
228
this.fnAddData = function( mData, bRedraw )
229
{
230
	if ( mData.length === 0 )
231
	{
232
		return [];
233
	}
234
	
235
	var aiReturn = [];
236
	var iTest;
237
	
238
	/* Find settings from table node */
239
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
240
	
241
	/* Check if we want to add multiple rows or not */
242
	if ( typeof mData[0] === "object" && mData[0] !== null )
243
	{
244
		for ( var i=0 ; i<mData.length ; i++ )
245
		{
246
			iTest = _fnAddData( oSettings, mData[i] );
247
			if ( iTest == -1 )
248
			{
249
				return aiReturn;
250
			}
251
			aiReturn.push( iTest );
252
		}
253
	}
254
	else
255
	{
256
		iTest = _fnAddData( oSettings, mData );
257
		if ( iTest == -1 )
258
		{
259
			return aiReturn;
260
		}
261
		aiReturn.push( iTest );
262
	}
263
	
264
	oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
265
	
266
	if ( bRedraw === undefined || bRedraw )
267
	{
268
		_fnReDraw( oSettings );
269
	}
270
	return aiReturn;
271
};
272
273
274
/**
275
 * This function will make DataTables recalculate the column sizes, based on the data 
276
 * contained in the table and the sizes applied to the columns (in the DOM, CSS or 
277
 * through the sWidth parameter). This can be useful when the width of the table's 
278
 * parent element changes (for example a window resize).
279
 *  @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
280
 *  @dtopt API
281
 *
282
 *  @example
283
 *    $(document).ready(function() {
284
 *      var oTable = $('#example').dataTable( {
285
 *        "sScrollY": "200px",
286
 *        "bPaginate": false
287
 *      } );
288
 *      
289
 *      $(window).bind('resize', function () {
290
 *        oTable.fnAdjustColumnSizing();
291
 *      } );
292
 *    } );
293
 */
294
this.fnAdjustColumnSizing = function ( bRedraw )
295
{
296
	var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
297
	_fnAdjustColumnSizing( oSettings );
298
	
299
	if ( bRedraw === undefined || bRedraw )
300
	{
301
		this.fnDraw( false );
302
	}
303
	else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
304
	{
305
		/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
306
		this.oApi._fnScrollDraw(oSettings);
307
	}
308
};
309
310
311
/**
312
 * Quickly and simply clear a table
313
 *  @param {bool} [bRedraw=true] redraw the table or not
314
 *  @dtopt API
315
 *
316
 *  @example
317
 *    $(document).ready(function() {
318
 *      var oTable = $('#example').dataTable();
319
 *      
320
 *      // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
321
 *      oTable.fnClearTable();
322
 *    } );
323
 */
324
this.fnClearTable = function( bRedraw )
325
{
326
	/* Find settings from table node */
327
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
328
	_fnClearTable( oSettings );
329
	
330
	if ( bRedraw === undefined || bRedraw )
331
	{
332
		_fnDraw( oSettings );
333
	}
334
};
335
336
337
/**
338
 * The exact opposite of 'opening' a row, this function will close any rows which 
339
 * are currently 'open'.
340
 *  @param {node} nTr the table row to 'close'
341
 *  @returns {int} 0 on success, or 1 if failed (can't find the row)
342
 *  @dtopt API
343
 *
344
 *  @example
345
 *    $(document).ready(function() {
346
 *      var oTable;
347
 *      
348
 *      // 'open' an information row when a row is clicked on
349
 *      $('#example tbody tr').click( function () {
350
 *        if ( oTable.fnIsOpen(this) ) {
351
 *          oTable.fnClose( this );
352
 *        } else {
353
 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
354
 *        }
355
 *      } );
356
 *      
357
 *      oTable = $('#example').dataTable();
358
 *    } );
359
 */
360
this.fnClose = function( nTr )
361
{
362
	/* Find settings from table node */
363
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
364
	
365
	for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
366
	{
367
		if ( oSettings.aoOpenRows[i].nParent == nTr )
368
		{
369
			var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
370
			if ( nTrParent )
371
			{
372
				/* Remove it if it is currently on display */
373
				nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
374
			}
375
			oSettings.aoOpenRows.splice( i, 1 );
376
			return 0;
377
		}
378
	}
379
	return 1;
380
};
381
382
383
/**
384
 * Remove a row for the table
385
 *  @param {mixed} mTarget The index of the row from aoData to be deleted, or
386
 *    the TR element you want to delete
387
 *  @param {function|null} [fnCallBack] Callback function
388
 *  @param {bool} [bRedraw=true] Redraw the table or not
389
 *  @returns {array} The row that was deleted
390
 *  @dtopt API
391
 *
392
 *  @example
393
 *    $(document).ready(function() {
394
 *      var oTable = $('#example').dataTable();
395
 *      
396
 *      // Immediately remove the first row
397
 *      oTable.fnDeleteRow( 0 );
398
 *    } );
399
 */
400
this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
401
{
402
	/* Find settings from table node */
403
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
404
	var i, iLen, iAODataIndex;
405
	
406
	iAODataIndex = (typeof mTarget === 'object') ? 
407
		_fnNodeToDataIndex(oSettings, mTarget) : mTarget;
408
	
409
	/* Return the data array from this row */
410
	var oData = oSettings.aoData.splice( iAODataIndex, 1 );
411
412
	/* Update the _DT_RowIndex parameter */
413
	for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
414
	{
415
		if ( oSettings.aoData[i].nTr !== null )
416
		{
417
			oSettings.aoData[i].nTr._DT_RowIndex = i;
418
		}
419
	}
420
	
421
	/* Remove the target row from the search array */
422
	var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
423
	oSettings.asDataSearch.splice( iDisplayIndex, 1 );
424
	
425
	/* Delete from the display arrays */
426
	_fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
427
	_fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
428
	
429
	/* If there is a user callback function - call it */
430
	if ( typeof fnCallBack === "function" )
431
	{
432
		fnCallBack.call( this, oSettings, oData );
433
	}
434
	
435
	/* Check for an 'overflow' they case for displaying the table */
436
	if ( oSettings._iDisplayStart >= oSettings.fnRecordsDisplay() )
437
	{
438
		oSettings._iDisplayStart -= oSettings._iDisplayLength;
439
		if ( oSettings._iDisplayStart < 0 )
440
		{
441
			oSettings._iDisplayStart = 0;
442
		}
443
	}
444
	
445
	if ( bRedraw === undefined || bRedraw )
446
	{
447
		_fnCalculateEnd( oSettings );
448
		_fnDraw( oSettings );
449
	}
450
	
451
	return oData;
452
};
453
454
455
/**
456
 * Restore the table to it's original state in the DOM by removing all of DataTables 
457
 * enhancements, alterations to the DOM structure of the table and event listeners.
458
 *  @param {boolean} [bRemove=false] Completely remove the table from the DOM
459
 *  @dtopt API
460
 *
461
 *  @example
462
 *    $(document).ready(function() {
463
 *      // This example is fairly pointless in reality, but shows how fnDestroy can be used
464
 *      var oTable = $('#example').dataTable();
465
 *      oTable.fnDestroy();
466
 *    } );
467
 */
468
this.fnDestroy = function ( bRemove )
469
{
470
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
471
	var nOrig = oSettings.nTableWrapper.parentNode;
472
	var nBody = oSettings.nTBody;
473
	var i, iLen;
474
475
	bRemove = (bRemove===undefined) ? false : bRemove;
476
	
477
	/* Flag to note that the table is currently being destroyed - no action should be taken */
478
	oSettings.bDestroying = true;
479
	
480
	/* Fire off the destroy callbacks for plug-ins etc */
481
	_fnCallbackFire( oSettings, "aoDestroyCallback", "destroy", [oSettings] );
482
483
	/* If the table is not being removed, restore the hidden columns */
484
	if ( !bRemove )
485
	{
486
		for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
487
		{
488
			if ( oSettings.aoColumns[i].bVisible === false )
489
			{
490
				this.fnSetColumnVis( i, true );
491
			}
492
		}
493
	}
494
	
495
	/* Blitz all DT events */
496
	$(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT');
497
	
498
	/* If there is an 'empty' indicator row, remove it */
499
	$('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
500
	
501
	/* When scrolling we had to break the table up - restore it */
502
	if ( oSettings.nTable != oSettings.nTHead.parentNode )
503
	{
504
		$(oSettings.nTable).children('thead').remove();
505
		oSettings.nTable.appendChild( oSettings.nTHead );
506
	}
507
	
508
	if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
509
	{
510
		$(oSettings.nTable).children('tfoot').remove();
511
		oSettings.nTable.appendChild( oSettings.nTFoot );
512
	}
513
	
514
	/* Remove the DataTables generated nodes, events and classes */
515
	oSettings.nTable.parentNode.removeChild( oSettings.nTable );
516
	$(oSettings.nTableWrapper).remove();
517
	
518
	oSettings.aaSorting = [];
519
	oSettings.aaSortingFixed = [];
520
	_fnSortingClasses( oSettings );
521
	
522
	$(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripeClasses.join(' ') );
523
	
524
	$('th, td', oSettings.nTHead).removeClass( [
525
		oSettings.oClasses.sSortable,
526
		oSettings.oClasses.sSortableAsc,
527
		oSettings.oClasses.sSortableDesc,
528
		oSettings.oClasses.sSortableNone ].join(' ')
529
	);
530
	if ( oSettings.bJUI )
531
	{
532
		$('th span.'+oSettings.oClasses.sSortIcon
533
			+ ', td span.'+oSettings.oClasses.sSortIcon, oSettings.nTHead).remove();
534
535
		$('th, td', oSettings.nTHead).each( function () {
536
			var jqWrapper = $('div.'+oSettings.oClasses.sSortJUIWrapper, this);
537
			var kids = jqWrapper.contents();
538
			$(this).append( kids );
539
			jqWrapper.remove();
540
		} );
541
	}
542
	
543
	/* Add the TR elements back into the table in their original order */
544
	if ( !bRemove && oSettings.nTableReinsertBefore )
545
	{
546
		nOrig.insertBefore( oSettings.nTable, oSettings.nTableReinsertBefore );
547
	}
548
	else if ( !bRemove )
549
	{
550
		nOrig.appendChild( oSettings.nTable );
551
	}
552
553
	for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
554
	{
555
		if ( oSettings.aoData[i].nTr !== null )
556
		{
557
			nBody.appendChild( oSettings.aoData[i].nTr );
558
		}
559
	}
560
	
561
	/* Restore the width of the original table */
562
	if ( oSettings.oFeatures.bAutoWidth === true )
563
	{
564
	  oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
565
	}
566
	
567
	/* If the were originally stripe classes - then we add them back here. Note
568
	 * this is not fool proof (for example if not all rows had stripe classes - but
569
	 * it's a good effort without getting carried away
570
	 */
571
	iLen = oSettings.asDestroyStripes.length;
572
	if (iLen)
573
	{
574
		var anRows = $(nBody).children('tr');
575
		for ( i=0 ; i<iLen ; i++ )
576
		{
577
			anRows.filter(':nth-child(' + iLen + 'n + ' + i + ')').addClass( oSettings.asDestroyStripes[i] );
578
		}
579
	}
580
	
581
	/* Remove the settings object from the settings array */
582
	for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
583
	{
584
		if ( DataTable.settings[i] == oSettings )
585
		{
586
			DataTable.settings.splice( i, 1 );
587
		}
588
	}
589
	
590
	/* End it all */
591
	oSettings = null;
0 ignored issues
show
Unused Code introduced by
The assignment to oSettings seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
592
	oInit = null;
0 ignored issues
show
Bug introduced by
The variable oInit seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.oInit.
Loading history...
593
};
594
595
596
/**
597
 * Redraw the table
598
 *  @param {bool} [bComplete=true] Re-filter and resort (if enabled) the table before the draw.
599
 *  @dtopt API
600
 *
601
 *  @example
602
 *    $(document).ready(function() {
603
 *      var oTable = $('#example').dataTable();
604
 *      
605
 *      // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
606
 *      oTable.fnDraw();
607
 *    } );
608
 */
609
this.fnDraw = function( bComplete )
610
{
611
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
612
	if ( bComplete === false )
613
	{
614
		_fnCalculateEnd( oSettings );
615
		_fnDraw( oSettings );
616
	}
617
	else
618
	{
619
		_fnReDraw( oSettings );
620
	}
621
};
622
623
624
/**
625
 * Filter the input based on data
626
 *  @param {string} sInput String to filter the table on
627
 *  @param {int|null} [iColumn] Column to limit filtering to
628
 *  @param {bool} [bRegex=false] Treat as regular expression or not
629
 *  @param {bool} [bSmart=true] Perform smart filtering or not
630
 *  @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
631
 *  @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
632
 *  @dtopt API
633
 *
634
 *  @example
635
 *    $(document).ready(function() {
636
 *      var oTable = $('#example').dataTable();
637
 *      
638
 *      // Sometime later - filter...
639
 *      oTable.fnFilter( 'test string' );
640
 *    } );
641
 */
642
this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
643
{
644
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
645
	
646
	if ( !oSettings.oFeatures.bFilter )
647
	{
648
		return;
649
	}
650
	
651
	if ( bRegex === undefined || bRegex === null )
652
	{
653
		bRegex = false;
654
	}
655
	
656
	if ( bSmart === undefined || bSmart === null )
657
	{
658
		bSmart = true;
659
	}
660
	
661
	if ( bShowGlobal === undefined || bShowGlobal === null )
662
	{
663
		bShowGlobal = true;
664
	}
665
	
666
	if ( bCaseInsensitive === undefined || bCaseInsensitive === null )
667
	{
668
		bCaseInsensitive = true;
669
	}
670
	
671
	if ( iColumn === undefined || iColumn === null )
672
	{
673
		/* Global filter */
674
		_fnFilterComplete( oSettings, {
675
			"sSearch":sInput+"",
676
			"bRegex": bRegex,
677
			"bSmart": bSmart,
678
			"bCaseInsensitive": bCaseInsensitive
679
		}, 1 );
680
		
681
		if ( bShowGlobal && oSettings.aanFeatures.f )
682
		{
683
			var n = oSettings.aanFeatures.f;
684
			for ( var i=0, iLen=n.length ; i<iLen ; i++ )
685
			{
686
				// IE9 throws an 'unknown error' if document.AktifElement is used
687
				// inside an iframe or frame...
688
				try {
689
					if ( n[i]._DT_Input != document.AktifElement )
690
					{
691
						$(n[i]._DT_Input).val( sInput );
692
					}
693
				}
694
				catch ( e ) {
695
					$(n[i]._DT_Input).val( sInput );
696
				}
697
			}
698
		}
699
	}
700
	else
701
	{
702
		/* Single column filter */
703
		$.extend( oSettings.aoPreSearchCols[ iColumn ], {
704
			"sSearch": sInput+"",
705
			"bRegex": bRegex,
706
			"bSmart": bSmart,
707
			"bCaseInsensitive": bCaseInsensitive
708
		} );
709
		_fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
710
	}
711
};
712
713
714
/**
715
 * Get the data for the whole table, an individual row or an individual cell based on the 
716
 * provided parameters.
717
 *  @param {int|node} [mRow] A TR row node, TD/TH cell node or an integer. If given as
718
 *    a TR node then the data source for the whole row will be returned. If given as a
719
 *    TD/TH cell node then iCol will be automatically calculated and the data for the
720
 *    cell returned. If given as an integer, then this is treated as the aoData internal
721
 *    data index for the row (see fnGetPosition) and the data for that row used.
722
 *  @param {int} [iCol] Optional column index that you want the data of.
723
 *  @returns {array|object|string} If mRow is undefined, then the data for all rows is
724
 *    returned. If mRow is defined, just data for that row, and is iCol is
725
 *    defined, only data for the designated cell is returned.
726
 *  @dtopt API
727
 *
728
 *  @example
729
 *    // Row data
730
 *    $(document).ready(function() {
731
 *      oTable = $('#example').dataTable();
732
 *
733
 *      oTable.$('tr').click( function () {
734
 *        var data = oTable.fnGetData( this );
735
 *        // ... do something with the array / object of data for the row
736
 *      } );
737
 *    } );
738
 *
739
 *  @example
740
 *    // Individual cell data
741
 *    $(document).ready(function() {
742
 *      oTable = $('#example').dataTable();
743
 *
744
 *      oTable.$('td').click( function () {
745
 *        var sData = oTable.fnGetData( this );
746
 *        alert( 'The cell clicked on had the value of '+sData );
747
 *      } );
748
 *    } );
749
 */
750
this.fnGetData = function( mRow, iCol )
751
{
752
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
753
	
754
	if ( mRow !== undefined )
755
	{
756
		var iRow = mRow;
757
		if ( typeof mRow === 'object' )
758
		{
759
			var sNode = mRow.nodeName.toLowerCase();
760
			if (sNode === "tr" )
761
			{
762
				iRow = _fnNodeToDataIndex(oSettings, mRow);
763
			}
764
			else if ( sNode === "td" )
765
			{
766
				iRow = _fnNodeToDataIndex(oSettings, mRow.parentNode);
767
				iCol = _fnNodeToColumnIndex( oSettings, iRow, mRow );
768
			}
769
		}
770
771
		if ( iCol !== undefined )
772
		{
773
			return _fnGetCellData( oSettings, iRow, iCol, '' );
774
		}
775
		return (oSettings.aoData[iRow]!==undefined) ?
776
			oSettings.aoData[iRow]._aData : null;
777
	}
778
	return _fnGetDataMaster( oSettings );
779
};
780
781
782
/**
783
 * Get an array of the TR nodes that are used in the table's body. Note that you will 
784
 * typically want to use the '$' API method in preference to this as it is more 
785
 * flexible.
786
 *  @param {int} [iRow] Optional row index for the TR element you want
787
 *  @returns {array|node} If iRow is undefined, returns an array of all TR elements
788
 *    in the table's body, or iRow is defined, just the TR element requested.
789
 *  @dtopt API
790
 *
791
 *  @example
792
 *    $(document).ready(function() {
793
 *      var oTable = $('#example').dataTable();
794
 *      
795
 *      // Get the nodes from the table
796
 *      var nNodes = oTable.fnGetNodes( );
797
 *    } );
798
 */
799
this.fnGetNodes = function( iRow )
800
{
801
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
802
	
803
	if ( iRow !== undefined ) {
804
		return (oSettings.aoData[iRow]!==undefined) ?
805
			oSettings.aoData[iRow].nTr : null;
806
	}
807
	return _fnGetTrNodes( oSettings );
808
};
809
810
811
/**
812
 * Get the array indexes of a particular cell from it's DOM element
813
 * and column index including hidden columns
814
 *  @param {node} nNode this can either be a TR, TD or TH in the table's body
815
 *  @returns {int} If nNode is given as a TR, then a single index is returned, or
816
 *    if given as a cell, an array of [row index, column index (visible), 
817
 *    column index (all)] is given.
818
 *  @dtopt API
819
 *
820
 *  @example
821
 *    $(document).ready(function() {
822
 *      $('#example tbody td').click( function () {
823
 *        // Get the position of the current data from the node
824
 *        var aPos = oTable.fnGetPosition( this );
825
 *        
826
 *        // Get the data array for this row
827
 *        var aData = oTable.fnGetData( aPos[0] );
828
 *        
829
 *        // Update the data array and return the value
830
 *        aData[ aPos[1] ] = 'clicked';
831
 *        this.innerHTML = 'clicked';
832
 *      } );
833
 *      
834
 *      // Init DataTables
835
 *      oTable = $('#example').dataTable();
836
 *    } );
837
 */
838
this.fnGetPosition = function( nNode )
839
{
840
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
841
	var sNodeName = nNode.nodeName.toUpperCase();
842
	
843
	if ( sNodeName == "TR" )
844
	{
845
		return _fnNodeToDataIndex(oSettings, nNode);
846
	}
847
	else if ( sNodeName == "TD" || sNodeName == "TH" )
848
	{
849
		var iDataIndex = _fnNodeToDataIndex( oSettings, nNode.parentNode );
850
		var iColumnIndex = _fnNodeToColumnIndex( oSettings, iDataIndex, nNode );
851
		return [ iDataIndex, _fnColumnIndexToVisible(oSettings, iColumnIndex ), iColumnIndex ];
852
	}
853
	return null;
854
};
855
856
857
/**
858
 * Check to see if a row is 'open' or not.
859
 *  @param {node} nTr the table row to check
860
 *  @returns {boolean} true if the row is currently open, false otherwise
861
 *  @dtopt API
862
 *
863
 *  @example
864
 *    $(document).ready(function() {
865
 *      var oTable;
866
 *      
867
 *      // 'open' an information row when a row is clicked on
868
 *      $('#example tbody tr').click( function () {
869
 *        if ( oTable.fnIsOpen(this) ) {
870
 *          oTable.fnClose( this );
871
 *        } else {
872
 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
873
 *        }
874
 *      } );
875
 *      
876
 *      oTable = $('#example').dataTable();
877
 *    } );
878
 */
879
this.fnIsOpen = function( nTr )
880
{
881
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
882
	var aoOpenRows = oSettings.aoOpenRows;
883
	
884
	for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
885
	{
886
		if ( oSettings.aoOpenRows[i].nParent == nTr )
887
		{
888
			return true;
889
		}
890
	}
891
	return false;
892
};
893
894
895
/**
896
 * This function will place a new row directly after a row which is currently 
897
 * on display on the page, with the HTML contents that is passed into the 
898
 * function. This can be used, for example, to ask for confirmation that a 
899
 * particular record should be deleted.
900
 *  @param {node} nTr The table row to 'open'
901
 *  @param {string|node|jQuery} mHtml The HTML to put into the row
902
 *  @param {string} sClass Class to give the new TD cell
903
 *  @returns {node} The row opened. Note that if the table row passed in as the
904
 *    first parameter, is not found in the table, this method will silently
905
 *    return.
906
 *  @dtopt API
907
 *
908
 *  @example
909
 *    $(document).ready(function() {
910
 *      var oTable;
911
 *      
912
 *      // 'open' an information row when a row is clicked on
913
 *      $('#example tbody tr').click( function () {
914
 *        if ( oTable.fnIsOpen(this) ) {
915
 *          oTable.fnClose( this );
916
 *        } else {
917
 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
918
 *        }
919
 *      } );
920
 *      
921
 *      oTable = $('#example').dataTable();
922
 *    } );
923
 */
924
this.fnOpen = function( nTr, mHtml, sClass )
925
{
926
	/* Find settings from table node */
927
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
928
929
	/* Check that the row given is in the table */
930
	var nTableRows = _fnGetTrNodes( oSettings );
931
	if ( $.inArray(nTr, nTableRows) === -1 )
932
	{
933
		return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
934
	}
935
	
936
	/* the old open one if there is one */
937
	this.fnClose( nTr );
938
	
939
	var nNewRow = document.createElement("tr");
940
	var nNewCell = document.createElement("td");
941
	nNewRow.appendChild( nNewCell );
942
	nNewCell.className = sClass;
943
	nNewCell.colSpan = _fnVisbleColumns( oSettings );
944
945
	if (typeof mHtml === "string")
946
	{
947
		nNewCell.innerHTML = mHtml;
948
	}
949
	else
950
	{
951
		$(nNewCell).html( mHtml );
952
	}
953
954
	/* If the nTr isn't on the page at the moment - then we don't insert at the moment */
955
	var nTrs = $('tr', oSettings.nTBody);
956
	if ( $.inArray(nTr, nTrs) != -1  )
957
	{
958
		$(nNewRow).insertAfter(nTr);
959
	}
960
	
961
	oSettings.aoOpenRows.push( {
962
		"nTr": nNewRow,
963
		"nParent": nTr
964
	} );
965
	
966
	return nNewRow;
967
};
968
969
970
/**
971
 * Change the pagination - provides the internal logic for pagination in a simple API 
972
 * function. With this function you can have a DataTables table go to the next, 
973
 * previous, first or last pages.
974
 *  @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
975
 *    or page number to jump to (integer), note that page 0 is the first page.
976
 *  @param {bool} [bRedraw=true] Redraw the table or not
977
 *  @dtopt API
978
 *
979
 *  @example
980
 *    $(document).ready(function() {
981
 *      var oTable = $('#example').dataTable();
982
 *      oTable.fnPageChange( 'next' );
983
 *    } );
984
 */
985
this.fnPageChange = function ( mAction, bRedraw )
986
{
987
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
988
	_fnPageChange( oSettings, mAction );
989
	_fnCalculateEnd( oSettings );
990
	
991
	if ( bRedraw === undefined || bRedraw )
992
	{
993
		_fnDraw( oSettings );
994
	}
995
};
996
997
998
/**
999
 * Show a particular column
1000
 *  @param {int} iCol The column whose display should be changed
1001
 *  @param {bool} bShow Show (true) or hide (false) the column
1002
 *  @param {bool} [bRedraw=true] Redraw the table or not
1003
 *  @dtopt API
1004
 *
1005
 *  @example
1006
 *    $(document).ready(function() {
1007
 *      var oTable = $('#example').dataTable();
1008
 *      
1009
 *      // Hide the second column after initialisation
1010
 *      oTable.fnSetColumnVis( 1, false );
1011
 *    } );
1012
 */
1013
this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
1014
{
1015
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
1016
	var i, iLen;
1017
	var aoColumns = oSettings.aoColumns;
1018
	var aoData = oSettings.aoData;
1019
	var nTd, bAppend, iBefore;
1020
	
1021
	/* No point in doing anything if we are requesting what is already true */
1022
	if ( aoColumns[iCol].bVisible == bShow )
1023
	{
1024
		return;
1025
	}
1026
	
1027
	/* Show the column */
1028
	if ( bShow )
1029
	{
1030
		var iInsert = 0;
1031
		for ( i=0 ; i<iCol ; i++ )
1032
		{
1033
			if ( aoColumns[i].bVisible )
1034
			{
1035
				iInsert++;
1036
			}
1037
		}
1038
		
1039
		/* Need to decide if we should use appendChild or insertBefore */
1040
		bAppend = (iInsert >= _fnVisbleColumns( oSettings ));
1041
1042
		/* Which coloumn should we be inserting before? */
1043
		if ( !bAppend )
1044
		{
1045
			for ( i=iCol ; i<aoColumns.length ; i++ )
1046
			{
1047
				if ( aoColumns[i].bVisible )
1048
				{
1049
					iBefore = i;
1050
					break;
1051
				}
1052
			}
1053
		}
1054
1055
		for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
1056
		{
1057
			if ( aoData[i].nTr !== null )
1058
			{
1059
				if ( bAppend )
1060
				{
1061
					aoData[i].nTr.appendChild( 
1062
						aoData[i]._anHidden[iCol]
1063
					);
1064
				}
1065
				else
1066
				{
1067
					aoData[i].nTr.insertBefore(
1068
						aoData[i]._anHidden[iCol], 
1069
						_fnGetTdNodes( oSettings, i )[iBefore] );
0 ignored issues
show
Bug introduced by
The variable iBefore seems to not be initialized for all possible execution paths.
Loading history...
1070
				}
1071
			}
1072
		}
1073
	}
1074
	else
1075
	{
1076
		/* Remove a column from display */
1077
		for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
1078
		{
1079
			if ( aoData[i].nTr !== null )
1080
			{
1081
				nTd = _fnGetTdNodes( oSettings, i )[iCol];
1082
				aoData[i]._anHidden[iCol] = nTd;
1083
				nTd.parentNode.removeChild( nTd );
1084
			}
1085
		}
1086
	}
1087
1088
	/* Clear to set the visible flag */
1089
	aoColumns[iCol].bVisible = bShow;
1090
1091
	/* Redraw the header and footer based on the new column visibility */
1092
	_fnDrawHead( oSettings, oSettings.aoHeader );
1093
	if ( oSettings.nTFoot )
1094
	{
1095
		_fnDrawHead( oSettings, oSettings.aoFooter );
1096
	}
1097
	
1098
	/* If there are any 'open' rows, then we need to alter the colspan for this col change */
1099
	for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
1100
	{
1101
		oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
1102
	}
1103
	
1104
	/* Do a redraw incase anything depending on the table columns needs it 
1105
	 * (built-in: scrolling) 
1106
	 */
1107
	if ( bRedraw === undefined || bRedraw )
1108
	{
1109
		_fnAdjustColumnSizing( oSettings );
1110
		_fnDraw( oSettings );
1111
	}
1112
	
1113
	_fnSaveState( oSettings );
1114
};
1115
1116
1117
/**
1118
 * Get the settings for a particular table for external manipulation
1119
 *  @returns {object} DataTables settings object. See 
1120
 *    {@link DataTable.models.oSettings}
1121
 *  @dtopt API
1122
 *
1123
 *  @example
1124
 *    $(document).ready(function() {
1125
 *      var oTable = $('#example').dataTable();
1126
 *      var oSettings = oTable.fnSettings();
1127
 *      
1128
 *      // Show an example parameter from the settings
1129
 *      alert( oSettings._iDisplayStart );
1130
 *    } );
1131
 */
1132
this.fnSettings = function()
1133
{
1134
	return _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
1135
};
1136
1137
1138
/**
1139
 * Sort the table by a particular column
1140
 *  @param {int} iCol the data index to sort on. Note that this will not match the 
1141
 *    'display index' if you have hidden data entries
1142
 *  @dtopt API
1143
 *
1144
 *  @example
1145
 *    $(document).ready(function() {
1146
 *      var oTable = $('#example').dataTable();
1147
 *      
1148
 *      // Sort immediately with columns 0 and 1
1149
 *      oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
1150
 *    } );
1151
 */
1152
this.fnSort = function( aaSort )
1153
{
1154
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
1155
	oSettings.aaSorting = aaSort;
1156
	_fnSort( oSettings );
1157
};
1158
1159
1160
/**
1161
 * Attach a sort listener to an element for a given column
1162
 *  @param {node} nNode the element to attach the sort listener to
1163
 *  @param {int} iColumn the column that a click on this node will sort on
1164
 *  @param {function} [fnCallback] callback function when sort is run
1165
 *  @dtopt API
1166
 *
1167
 *  @example
1168
 *    $(document).ready(function() {
1169
 *      var oTable = $('#example').dataTable();
1170
 *      
1171
 *      // Sort on column 1, when 'sorter' is clicked on
1172
 *      oTable.fnSortListener( document.getElementById('sorter'), 1 );
1173
 *    } );
1174
 */
1175
this.fnSortListener = function( nNode, iColumn, fnCallback )
1176
{
1177
	_fnSortAttachListener( _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ), nNode, iColumn,
1178
	 	fnCallback );
1179
};
1180
1181
1182
/**
1183
 * Update a table cell or row - this method will accept either a single value to
1184
 * update the cell with, an array of values with one element for each column or
1185
 * an object in the same format as the original data source. The function is
1186
 * self-referencing in order to make the multi column updates easier.
1187
 *  @param {object|array|string} mData Data to update the cell/row with
1188
 *  @param {node|int} mRow TR element you want to update or the aoData index
1189
 *  @param {int} [iColumn] The column to update, give as null or undefined to
1190
 *    update a whole row.
1191
 *  @param {bool} [bRedraw=true] Redraw the table or not
1192
 *  @param {bool} [bAction=true] Perform pre-draw actions or not
1193
 *  @returns {int} 0 on success, 1 on error
1194
 *  @dtopt API
1195
 *
1196
 *  @example
1197
 *    $(document).ready(function() {
1198
 *      var oTable = $('#example').dataTable();
1199
 *      oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
1200
 *      oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
1201
 *    } );
1202
 */
1203
this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
1204
{
1205
	var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
1206
	var i, sDisplay;
1207
	var iRow = (typeof mRow === 'object') ? 
1208
		_fnNodeToDataIndex(oSettings, mRow) : mRow;
1209
1210
	if ( iColumn === undefined || iColumn === null )
1211
	{
1212
		/* Update the whole row */
1213
		oSettings.aoData[iRow]._aData = mData;
1214
1215
		for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
1216
		{
1217
			this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
1218
		}
1219
	}
1220
	else
1221
	{
1222
		/* Individual cell update */
1223
		_fnSetCellData( oSettings, iRow, iColumn, mData );
1224
		sDisplay = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
1225
		
1226
		var oCol = oSettings.aoColumns[iColumn];
0 ignored issues
show
Unused Code introduced by
The variable oCol seems to be never used. Consider removing it.
Loading history...
1227
		if ( oSettings.aoData[iRow].nTr !== null )
1228
		{
1229
			/* Do the actual HTML update */
1230
			_fnGetTdNodes( oSettings, iRow )[iColumn].innerHTML = sDisplay;
1231
		}
1232
	}
1233
	
1234
	/* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
1235
	 * will rebuild the search array - however, the redraw might be disabled by the user)
1236
	 */
1237
	var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
1238
	oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow(
1239
		oSettings, 
1240
		_fnGetRowData( oSettings, iRow, 'filter', _fnGetColumns( oSettings, 'bSearchable' ) )
1241
	);
1242
	
1243
	/* Perform pre-draw actions */
1244
	if ( bAction === undefined || bAction )
1245
	{
1246
		_fnAdjustColumnSizing( oSettings );
1247
	}
1248
	
1249
	/* Redraw the table */
1250
	if ( bRedraw === undefined || bRedraw )
1251
	{
1252
		_fnReDraw( oSettings );
1253
	}
1254
	return 0;
1255
};
1256
1257
1258
/**
1259
 * Provide a common method for plug-ins to check the version of DataTables being used, in order
1260
 * to ensure compatibility.
1261
 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
1262
 *    formats "X" and "X.Y" are also acceptable.
1263
 *  @returns {boolean} true if this version of DataTables is greater or equal to the required
1264
 *    version, or false if this version of DataTales is not suitable
1265
 *  @method
1266
 *  @dtopt API
1267
 *
1268
 *  @example
1269
 *    $(document).ready(function() {
1270
 *      var oTable = $('#example').dataTable();
1271
 *      alert( oTable.fnVersionCheck( '1.9.0' ) );
1272
 *    } );
1273
 */
1274
this.fnVersionCheck = DataTable.ext.fnVersionCheck;
1275
1276